iT邦幫忙

2024 iThome 鐵人賽

DAY 21
0
Modern Web

前後端整合,用Spring boot 與React 開發屬於自己的記帳網頁系列 第 21

Day21 Spring boot 後端 開發 篩選類別功能API

  • 分享至 

  • xImage
  •  

前言

前一天的課程把前端與後端的重要功能按照順序開發完成,接下來我們來利用這個觀念,進行一些變化,算是更加進階一點的技術,而且整個開發的過程要從後端開始,一路開發到前端。
我相信對大家來說這個應該會是一個挑戰,但It鐵人賽就是來挑戰自我的地方,所以就讓我們來挑戰一下吧!
今天的功能,是開發根據類別篩選清單的功能,我們今天的目標是要開發完後端Spring boot的程式碼,就讓我們開始吧

Spring boot 篩選類別功能開發

依照前幾天的教學,在Spring boot的後端開發的順序是先從Repository->Serivce->ServiceImpl->Controller這個順序,而因為我們使用了JPA來當作Repository,JPA已經幫我們開發好很多的東西,我們就可以從Service開始
所以我們來到Service這個頁面中新增以下的功能

	List<String> getAllCategories();

再來到ServiceImpl層,我們這裡的功能是取得所有的類別,邏輯上是先讀取所有的資料,然後把資料做篩選,取得Category的資料回傳到前端,所以我們事先使用finall這個功能,然後用stream的方式把所有資料一筆筆取出來,用map的方式取得所有category,然後分區之後再用collect的方式取得資料。

	@Override

	public List<String> getAllCategories() {

	       List<Account> accounts = accountRepository.findAll();

	        return accounts.stream()

	                .map(Account::getCategory)

	                .distinct()

	                .collect(Collectors.toList());

	}

完成之後我們回到Controller,新增取得category的功能

    @GetMapping("/categories")

    public List<String> getAllCategories() {

        return accountsService.getAllCategories();

    }

Postman測試

再來就是來測試,只要回傳了你先前設置的清單即可
https://ithelp.ithome.com.tw/upload/images/20241001/20152864a05r0I7p4r.png

接下來,我們就要到前端繼續開發了

新增Service

我們要再React Service中心層新增這個功能,我們這個頁面會先從後端中取得所有的類別,給使用者選取,再來根據選取到的資料丟回給Spring boot,從Spring boot中取得所有這個類別的資料,所以我們要新增getAllCategories跟getAccountsByCategory這兩個功能

export const getAllCategories = ()=>axios.get(BASE_REST_API_URL+'/categories')
export const getAccountsByCategory = (category)=>axios.get(BASE_REST_API_URL+'/category/'+category)

新增篩選頁面

再來,我們就要新增篩選的頁面,我們要按照先前的邏輯,要引用useState, useEffect這些控制網頁參數與變動的功能,以及使用到我們開發的getAllCategories, getAccountsByCategory 兩個功能。
我們在這裡要使用到的參數有categories、selectedCategory跟最後從Spring取得的filteredAccounts
在網頁讀取的時候,我們就要先取得所有的類別,再把這些類別顯示到網頁中。
完整的程式碼如下

import React from 'react'
import { useState, useEffect } from 'react';
import { getAllCategories, getAccountsByCategory } from '../Service/AccountService';
const SortAccountComponent = () => {
    const [categories, setCategories] = useState([]);
    const [selectedCategory, setSelectedCategory] = useState('');
    const [filteredAccounts, setFilteredAccounts] = useState([]);
    // 取得所有的 Category
    useEffect(() => {
        getAllCategories().then((response) => {
            setCategories(response.data);
        }).catch((error) => {
            console.error(error);
        });
    }, []);
    // 當選擇的 Category 改變時,取得對應的帳戶清單
    useEffect(() => {
        if (selectedCategory) {
            getAccountsByCategory(selectedCategory).then((response) => {
                setFilteredAccounts(response.data);
            }).catch((error) => {
                console.error(error);
            });
        }
    }, [selectedCategory]);
  return (
    <div className='container'>
    <br></br>
    <div className='card col-md-6 offest-md-3 offset-md-3'>
        <br></br>
        <h2 className='text-center'>Sort Accounts by Category</h2>
        <div className='card-body'>
            <div className='form-group mb-2 text-center'>
                <label className='form-label'>Select Category:</label>
                <select
                    className='form-control'
                    value={selectedCategory}
                    onChange={(e) => setSelectedCategory(e.target.value)}
                >
                    <option value=''>Select a Category</option>
                    {categories.map((category, index) => (
                        <option key={index} value={category}>
                            {category}
                        </option>
                    ))}
                </select>
            </div>
            <br></br>
            {selectedCategory && (
                <div>
                    <h3 className='text-center'>Accounts in {selectedCategory}</h3>
                    <ul className='list-group'>
                        {filteredAccounts.map((account) => (
                            <li key={account.id} className='list-group-item'>
                                <strong>{account.name}</strong> - ${account.amount} ({account.expensed ? 'Expense' : 'Income'})
                            </li>
                        ))}
                    </ul>
                </div>
            )}
        </div>
    </div>
</div>
  )
}
export default SortAccountComponent

因為前後端整合是一件比較艱深的內容,所以我會預期大家先有html、CSS、JS的基礎,但這邊也還是多補充解釋一些前端網頁的呈現內容

                <select
                    className='form-control'
                    value={selectedCategory}
                    onChange={(e) => setSelectedCategory(e.target.value)}
                >
                    <option value=''>Select a Category</option>
                    {categories.map((category, index) => (
                        <option key={index} value={category}>
                            {category}
                        </option>
                    ))}
                </select>

這一段程式碼是顯示選擇的欄位,將讀取到的categories用category跟id的形式呈現,對外顯示類別
再來就是當我們選擇之後,就會出現一個selectCategory,那就會顯示以下的內容

 {selectedCategory && (
                <div>
                    <h3 className='text-center'>Accounts in {selectedCategory}</h3>
                    <ul className='list-group'>
                        {filteredAccounts.map((account) => (
                            <li key={account.id} className='list-group-item'>
                                <strong>{account.name}</strong> - ${account.amount} ({account.expensed ? 'Expense' : 'Income'})

這樣一來就能夠完成我們的目的,再來,我們要去App.jsx去設地這個頁面對應的網址

          {/* http://localhost:8080/sort-account/ */}
          <Route path='/sort-account' element={<SortAccountComponent />}></Route>

完整的程式碼如下

  return (
    <>
    <BrowserRouter>
        <HeaderComponent />
        <Routes>
        {/* http://localhost:8080/ */}
        <Route path='/' element={<ListAccountComponent />}></Route>
          {/* http://localhost:8080/weekly-account' */}
          <Route path='/week-account' element={<WeeklyAccountComponent />}></Route>
          {/* http://localhost:8080/list-account */}
          <Route path='/lsit-account' element={<ListAccountComponent />}></Route>
          {/* http://localhost:8080/add-account */}
          <Route path='/add-account' element={<AccountComponent />}></Route>
          {/* http://localhost:8080/update-account/1 */}
          <Route path='/update-account/:id' element={<AccountComponent />}></Route>
          {/* http://localhost:8080/sort-account/ */}
          <Route path='/sort-account' element={<SortAccountComponent />}></Route>
        </Routes>
        <FooterComponent />
    </BrowserRouter>
    </>
  )

結果如下
https://ithelp.ithome.com.tw/upload/images/20241001/20152864YiIATRWK0s.png
點選類別,然後隨便選擇一個類別
https://ithelp.ithome.com.tw/upload/images/20241001/201528645zlYktzchq.png
選擇好之後就會出現所有的清單
https://ithelp.ithome.com.tw/upload/images/20241001/201528647wKx9gyJ1i.png
到這邊,恭喜你完成了這段進階的功能!


上一篇
Day20 前端React開發:更新記帳資訊與刪除資料
下一篇
Day22 React前端網頁- 網址連結以及設置Header連結
系列文
前後端整合,用Spring boot 與React 開發屬於自己的記帳網頁30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言